#include "comm/znx_yaml_parser.h"
#include "comm/znx_log.h"

const char *str =
    "a: 1\n"
    "b: true\n"
    "c: aaaa bbbb\n"
    "d:\n"
    "   - 111\n"
    "   - 222\n"
    "   - 333\n"
    "e:\n"
    "   e1: 1\n"
    "   e2: true\n"
    "   e3: aaaaa bbbb\n";

static znx_bool_t
znx_test_parse_map(
    void *conf, znx_pool_t *pool, znx_command_t *command,
    yaml_document_t *doc, yaml_node_t *node, znx_str_t *err);
static znx_bool_t
znx_test_parse_array(
    void *conf, znx_pool_t *pool, znx_command_t *command,
    yaml_document_t *doc, yaml_node_t *node, znx_str_t *err);


typedef struct {
    int64_t             e1;
    znx_bool_t          e2;
    znx_str_t           e3;
} znx_test_e_t;


typedef struct {
    int64_t             a;
    znx_bool_t          b;
    znx_str_t           c;
    znx_array_t         *d;
    znx_test_e_t        e;
} znx_test_t;


znx_command_t           test_e_commands[] = {
    {
        ZNX_STRING("e1"),
        znx_command_set_int64,
        offsetof(znx_test_e_t, e1)
    },

    {
        ZNX_STRING("e2"),
        znx_command_set_bool,
        offsetof(znx_test_e_t, e2)
    },

    {
        ZNX_STRING("e3"),
        znx_command_set_string,
        offsetof(znx_test_e_t, e3)
    },

    znx_null_command
};


znx_command_t           test_commands[] = {
    {
        ZNX_STRING("a"),
        znx_command_set_int64,
        offsetof(znx_test_t, a)
    },

    {
        ZNX_STRING("b"),
        znx_command_set_bool,
        offsetof(znx_test_t, b)
    },

    {
        ZNX_STRING("c"),
        znx_command_set_string,
        offsetof(znx_test_t, c)
    },

    {
        ZNX_STRING("d"),
        znx_test_parse_array,
        0
    },

    {
        ZNX_STRING("e"),
        znx_test_parse_map,
        0
    },

    znx_null_command
};


static znx_bool_t
znx_test_parse_map(
    void *conf, znx_pool_t *pool, znx_command_t *command,
    yaml_document_t *doc, yaml_node_t *node, znx_str_t *err)
{
    znx_test_t  *test = conf;

    size_t err_buf_len = command->name.len + 128;

    if (node->type != YAML_MAPPING_NODE) {
        err->data = znx_pool_malloc(pool, err_buf_len);
        err->len = (size_t)(znx_snprintf(err->data, err_buf_len,
            "command %v value node is not map node", &command->name) - err->data);
        return ZNX_FALSE;
    }

    if (!znx_yaml_map_node_parser(&test->e, pool, doc, node, test_e_commands, err)) {
        return ZNX_FALSE;
    }

    return ZNX_TRUE;
}


static znx_bool_t
znx_test_parse_array(
    void *conf, znx_pool_t *pool, znx_command_t *command,
    yaml_document_t *doc, yaml_node_t *node, znx_str_t *err)
{
    znx_test_t  *test = conf;

    size_t err_buf_len = command->name.len + 128;

    if (node->type != YAML_SEQUENCE_NODE) {
        err->data = znx_pool_malloc(pool, err_buf_len);
        err->len = (size_t)(znx_snprintf(err->data, err_buf_len,
            "command %v value node is not sequence node", &command->name) - err->data);
        return ZNX_FALSE;
    }

    yaml_node_item_t                    *item;
    yaml_node_t                         *value_node;
    int64_t                             value, *p;
    znx_str_t                           tmp;

    for (item = node->data.sequence.items.start;
        item < node->data.sequence.items.top;
        item++)
    {
        value_node = yaml_document_get_node(doc, *item);

        if (value_node->type != YAML_SCALAR_NODE) {
            err->data = znx_pool_malloc(pool, err_buf_len);
            err->len = (size_t)(znx_snprintf(err->data, err_buf_len,
                "command %v found no scalar node element", &command->name) - err->data);
            return ZNX_FALSE;
        }

        tmp.data = (u_char *)value_node->data.scalar.value;
        tmp.len = value_node->data.scalar.length;

        if (!znx_str_to_int64(&tmp, &value)) {
            err->data = znx_pool_malloc(pool, err_buf_len);
            err->len = (size_t)(znx_snprintf(err->data, err_buf_len,
                "command %v found no int64 element", &command->name) - err->data);
            return ZNX_FALSE;
        }

        p = znx_array_push(test->d);
        *p = value;
    }

    return ZNX_TRUE;
}


int main()
{
    znx_test_t          test;
    znx_str_t           buffer;
    znx_pool_t          *pool;
    znx_str_t           err;

    bzero(&test, sizeof(znx_test_t));
    ZNX_STR_SET(&buffer, str);

    test.d = znx_array_create(2, sizeof(int64_t));
    pool = znx_pool_create_ext(ZNX_POOL_MIN_PAGE_SIZE);

    if (!znx_yaml_map_string_parser(&test, pool, &buffer, test_commands, &err)) {
        ZNX_DEF_LOG_FATAL(NULL, "expect true, but got false. err: %v", &err);
    }

    if (test.a != 1) {
        ZNX_DEF_LOG_FATAL(NULL, "expect 1, but got %d", test.a);
    }

    if (test.b != ZNX_TRUE) {
        ZNX_DEF_LOG_FATAL(NULL, "expect true, but got %d", test.b);
    }

    if (test.c.len != 9 || znx_strncmp(test.c.data, "aaaa bbbb", test.c.len) != 0) {
        ZNX_DEF_LOG_FATAL(NULL, "expect aaaa bbbb, but got %v", &test.c);
    }

    if (test.d->nelts != 3) {
        ZNX_DEF_LOG_FATAL(NULL, "expect 3, but got %d", test.d->nelts);
    }

    if (test.e.e1 != 1) {
        ZNX_DEF_LOG_FATAL(NULL, "expect 1, but got %d", test.e.e1);
    }

    if (test.e.e2 != ZNX_TRUE) {
        ZNX_DEF_LOG_FATAL(NULL, "expect 1, but got %d", test.e.e1);
    }

    if (test.e.e3.len != 10 ||
        znx_strncmp(test.e.e3.data, "aaaaa bbbb", test.e.e3.len) != 0)
    {
        ZNX_DEF_LOG_FATAL(NULL, "expect aaaa bbbb, but got %v", &test.e.e3);
    }

    znx_pool_destroy(pool);
    znx_array_destroy(test.d);

    return 0;
}